package org.lc.stk.service.baseInfo.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import org.lc.stk.dao.baseInfo.CengciRepository;
import org.lc.stk.dao.baseInfo.MajorCengciRepository;
import org.lc.stk.dao.baseInfo.MajorRepository;
import org.lc.stk.model.baseInfo.Cengci;
import org.lc.stk.model.baseInfo.Major;
import org.lc.stk.model.baseInfo.MajorCengci;
import org.lc.stk.service.baseInfo.MajorCengciService;
import org.lc.stk.service.exception.BusinessException;
import org.lc.stk.web.dto.majorcengci.CreateMajorCengciRequest;
import org.lc.stk.web.dto.majorcengci.MajorCengciQuery;
import org.lc.stk.web.dto.majorcengci.UpdateMajorCengciRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import jakarta.persistence.criteria.Predicate;

@Service
@Transactional(readOnly = true)
public class MajorCengciServiceImpl implements MajorCengciService {

    private static final Logger logger = LoggerFactory.getLogger(MajorCengciServiceImpl.class);

    @Autowired
    private MajorCengciRepository majorCengciRepository;

    @Autowired
    private MajorRepository majorRepository;

    @Autowired
    private CengciRepository cengciRepository;

    @Override
    @Transactional
    public MajorCengci create(CreateMajorCengciRequest request) {
        // 验证专业是否存在
        Major major = majorRepository.findById(request.getMajorId())
                .orElseThrow(() -> new BusinessException("专业不存在：" + request.getMajorId()));

        // 验证培养层次是否存在
        Cengci cengci = cengciRepository.findById(request.getCengciId())
                .orElseThrow(() -> new BusinessException("培养层次不存在：" + request.getCengciId()));

        // 检查是否已存在相同的关联
        if (exists(request.getMajorId(), request.getCengciId(), null)) {
            throw new BusinessException("该专业已关联此培养层次");
        }

        MajorCengci majorCengci = new MajorCengci();
        majorCengci.setMajor(major);
        majorCengci.setCengci(cengci);
        majorCengci.setOrderNum(request.getOrderNum());
        majorCengci.setRemark(request.getRemark());

        logger.info("Creating new MajorCengci: major={}, cengci={}", major.getCode(), cengci.getCode());
        return majorCengciRepository.save(majorCengci);
    }

    @Override
    @Transactional
    public MajorCengci update(UpdateMajorCengciRequest request) {
        MajorCengci existing = getById(request.getId());
        
        // 如果要更改关联的专业
        if (request.getMajorId() != null && !request.getMajorId().equals(existing.getMajor().getId())) {
            Major major = majorRepository.findById(request.getMajorId())
                    .orElseThrow(() -> new BusinessException("专业不存在：" + request.getMajorId()));
            existing.setMajor(major);
        }
        
        // 如果要更改关联的培养层次
        if (request.getCengciId() != null && !request.getCengciId().equals(existing.getCengci().getId())) {
            Cengci cengci = cengciRepository.findById(request.getCengciId())
                    .orElseThrow(() -> new BusinessException("培养层次不存在：" + request.getCengciId()));
            existing.setCengci(cengci);
        }

        // 如果更改了关联，检查是否会导致重复
        if ((request.getMajorId() != null && !request.getMajorId().equals(existing.getMajor().getId())) ||
            (request.getCengciId() != null && !request.getCengciId().equals(existing.getCengci().getId()))) {
            if (exists(
                request.getMajorId() != null ? request.getMajorId() : existing.getMajor().getId(),
                request.getCengciId() != null ? request.getCengciId() : existing.getCengci().getId(),
                request.getId()
            )) {
                throw new BusinessException("该专业已关联此培养层次");
            }
        }

        if (request.getOrderNum() != null) {
            existing.setOrderNum(request.getOrderNum());
        }
        if (request.getRemark() != null) {
            existing.setRemark(request.getRemark());
        }

        logger.info("Updating MajorCengci: id={}", request.getId());
        return majorCengciRepository.save(existing);
    }

    @Override
    @Transactional
    public void delete(Integer id) {
        // 验证是否存在
        MajorCengci majorCengci = getById(id);
        logger.info("Soft deleting MajorCengci: id={}", id);
        majorCengci.setState(MajorCengci.MajorCengciState.ACTIVE);
        majorCengciRepository.save(majorCengci);
    }

    @Override
    @Transactional
    public int deleteByMajorId(Integer majorId) {
        // 验证专业是否存在
        if (!majorRepository.existsById(majorId)) {
            throw new BusinessException("专业不存在：" + majorId);
        }
        logger.info("Batch soft deleting MajorCengci by majorId: {}", majorId);
        List<MajorCengci> list = getByMajorId(majorId);
        list.forEach(mc -> mc.setState(MajorCengci.MajorCengciState.ACTIVE));
        majorCengciRepository.saveAll(list);
        return list.size();
    }

    @Override
    @Transactional
    public int deleteByCengciId(Integer cengciId) {
        // 验证培养层次是否存在
        if (!cengciRepository.existsById(cengciId)) {
            throw new BusinessException("培养层次不存在：" + cengciId);
        }
        logger.info("Batch soft deleting MajorCengci by cengciId: {}", cengciId);
        List<MajorCengci> list = getByCengciId(cengciId);
        list.forEach(mc -> mc.setState(MajorCengci.MajorCengciState.ACTIVE));
        majorCengciRepository.saveAll(list);
        return list.size();
    }

    @Override
    public MajorCengci getById(Integer id) {
        return majorCengciRepository.findById(id)
                .orElseThrow(() -> new BusinessException("专业层次关联不存在：" + id));
    }

    @Override
    public List<MajorCengci> getByMajorId(Integer majorId) {
        return majorCengciRepository.findByMajorIdOrderByOrderNumAsc(majorId);
    }

    @Override
    public List<MajorCengci> getByCengciId(Integer cengciId) {
        return majorCengciRepository.findByCengciIdOrderByOrderNumAsc(cengciId);
    }

    @Override
    public Page<MajorCengci> query(MajorCengciQuery query, Pageable pageable) {
        return majorCengciRepository.findAll(buildSpecification(query), pageable);
    }

    @Override
    public boolean exists(Integer majorId, Integer cengciId, Integer excludeId) {
        Optional<MajorCengci> existing = majorCengciRepository.findByMajorAndCengci(
            majorRepository.getReferenceById(majorId),
            cengciRepository.getReferenceById(cengciId)
        );
        
        return existing.isPresent() && 
               (excludeId == null || !existing.get().getId().equals(excludeId));
    }

    private Specification<MajorCengci> buildSpecification(MajorCengciQuery query) {
        return (root, criteriaQuery, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (query.getMajorId() != null) {
                predicates.add(cb.equal(root.get("major").get("id"), query.getMajorId()));
            }

            if (StringUtils.hasText(query.getMajorCode())) {
                predicates.add(cb.like(
                    cb.lower(root.get("major").get("code")),
                    "%" + query.getMajorCode().toLowerCase() + "%"
                ));
            }

            if (StringUtils.hasText(query.getMajorName())) {
                predicates.add(cb.like(
                    cb.lower(root.get("major").get("name")),
                    "%" + query.getMajorName().toLowerCase() + "%"
                ));
            }

            if (query.getCengciId() != null) {
                predicates.add(cb.equal(root.get("cengci").get("id"), query.getCengciId()));
            }

            if (StringUtils.hasText(query.getCengciCode())) {
                predicates.add(cb.like(
                    cb.lower(root.get("cengci").get("code")),
                    "%" + query.getCengciCode().toLowerCase() + "%"
                ));
            }

            if (StringUtils.hasText(query.getCengciName())) {
                predicates.add(cb.like(
                    cb.lower(root.get("cengci").get("name")),
                    "%" + query.getCengciName().toLowerCase() + "%"
                ));
            }

            return cb.and(predicates.toArray(new Predicate[0]));
        };
    }
}
